預設繫結 ( default binding ) 是這四個規則中優先序最低的。
Test 1 隱含繫結 和 明確繫結
function foo(){
console.log( this.a );
}
var obj1 = {
a: 2,
foo: foo
};
var obj2 = {
a: 3,
foo: foo
}
obj1.foo(); // 2
obj1.foo.call( obj2 ); // 3
從結算的流程來看
明確的繫結會大於隱含的繫結。
(這個結論 Tony 不是很喜歡。因為照著邏輯結算,也是得到這樣的結果。之後再補上測試流程好了。)
Test 2 隱含繫結 + new 繫結
function foo( something ) {
this.a = something;
}
var obj1 = {
foo:foo
};
var obj2 = {}
obj1.foo( 2 );
console.log( obj1.a ); // 2
obj1.foo.call(obj2, 3);
console.log( obj2 ); // { a: 3 }
var bar = new obj1.foo( 4 );
console.log( obj1.a ); // 2
console.log( bar.a ); // 4
隱含的繫結是在執行函式,把值賦予當下的物件。
這時候的 new obj1.foo(4) 會把值灌給 obj1 嗎?
不會。
而是直接把 this 指向 bar。
這時候呼叫 bar,就會得到 4。
new 會大於隱含的繫結。
Test 3 硬繫結 vs New
function foo( something ) {
this.a = something;
}
var obj1 = {};
var bar = foo.bind( obj1 );
bar( 2 );
console.log( obj1.a ); // 2
var baz = new bar( 3 );
console.log( obj1.a ); // 2
console.log( baz.a ); // 3
這個例子 只要呼叫 bar 就會硬繫結到 obj1。
但是在 new bar( 3 ) this 還是會指向 obj1 嗎?
不會。
new 繫結 會大於 硬繫結。
如果塞在 call、apply、bind 裡面的是 null、undefined。等同於忽略這些繫結。
function foo() {
console.log( this.a );
}
var a = 2;
foo.call( null ); // 2
什麼情況會塞 null 在 call 裡面啊?
null 是佔位值。
function foo(a,b) {
console.log( "a: " + a + ", b: " + b);
// 輸出 "a: a, b: b"
}
foo.apply( null, [2, 3]); // a: 2, b: 3
var bar = foo.bind( null, 2 );
bar( 3 ); // a: 2, b: 3
用 apply(..) 當作是將陣列拆開,變成參數輸入很常用。
用 bind(..) 。
但你就是需要忽略 this 的存在。請輸入 null。
BUT!
如果你使用的 API 裡面有函式掛著 this ,但是你依然用了 null 去佔位。結果真的找到了他全域的變數,就會很麻煩了。
你可以設定一個等同於 null 的值 DMZ (demilitarized zone 非軍事區 ) 去佔位。
然後在自己的全域也設定一個,這時候 this 就會指向 DMZ。
他是個物件,帶著你喜愛的符號。作者推薦 mac 的 option + o,可以得到 ø。
function foo(a,b) {
console.log( "a: " + a + ", b: " + b);
}
var ø = Object.create( null );
foo.apply( ø, [2, 3] ); // a: 2, b: 3
var bar = foo.bind( ø, 2 );
bar( 3 ); // a: 2, b: 3
這時候,你的 this 都會指向 DMZ。就算是預設繫結也沒問題。
function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2
p 間接參考 o。
因為 p 沒有 foo,會由 o.foo 傳入,建立一個 foo: foo。
但這時候的執行位置,還是全域。(把它看成是 IIFE 吧。)
硬繫結太硬,只有 new 可以破。
有其他比較軟一點方法呢? 用隱含和明確就可以修改。
書上有給軟繫結的工具,有興趣請看書上 softBind 的程式碼。
實際的運用,Tony 需要點時間了解,所以...
ES6 引進的新規格:箭頭函式 ( arrow-function )
他用的規則和前面四個沒關係,箭頭函式是用語彙範疇。
( 這不就打之前 this or that 的嘴巴嗎?)
function foo() {
return (a) => {
console.log( this.a );
}
}
var obj1 = { a: 2 };
var obj2 = { a: 3 };
var bar = foo.call( obj1 );
bar.call( obj2 ); // 2,不是 3 !
下面還有兩個範例。
但主要都請以兩個方法作結
this 是個神奇的旅程。
但最後兩個部分,的確有點難懂,特別是軟化繫結。
也剛好 Tony 對 setTimeout 有莫名的障礙。
但大致上都還是可以藉由閱讀程式碼了解文章內容。
好範例真的很重要。
那就
明天見啦~